#include "http.h"

char    *http_make_current_date(char *buffer, int size)
{
  time_t curtime;
  struct tm *gmt;

  curtime = time (&curtime);
  gmt = gmtime(&curtime);
  strftime (buffer, size, "%a, %d %b %G %H:%M:%S GMT", gmt);
  return buffer;

}

nstr_t  *http_make_header(http_request_t *request,
                          int status_code, const char *content_type
                          , int content_size, nstr_t *msg)
{
  char  date_buff[256];

  switch(status_code)
    {
    case 200:
      msg = nstr_addstr(msg, "HTTP/1.1 200 OK\r\n");
      break;
    case 201:
      msg = nstr_addstr(msg, "HTTP/1.1 201 Created\r\n");
      break;
    case 202:
      msg = nstr_addstr(msg, "HTTP/1.1 202 Accepted\r\n");
      break;
    case 400:
      msg = nstr_addstr(msg, "HTTP/1.1 400 Bad Request\r\n");
      break;
    case 401:
      msg = nstr_addstr(msg, "HTTP/1.1 401 Unauthorized\r\n");
      break;
    case 403:
      msg = nstr_addstr(msg, "HTTP/1.1 403 Forbidden\r\n");
      break;
    case 404:
      msg = nstr_addstr(msg, "HTTP/1.1 404 Not Found\r\n");
      break;
    default:
      msg = nstr_addstr(msg, "HTTP/1.1 500 Internal Server Error\r\n");
      break;
    }

  msg = nstr_add(msg, "Date: %s\r\n", http_make_current_date(date_buff, 256));

  
  if(content_size < 0)
    msg = nstr_add(msg
                   , "Content-Type: %s\r\nTransfer-Encoding: chunked\r\n\r\n"
                   , content_type);
  else
    msg = nstr_add(msg
                   , "Content-Type: %s\r\nContent-Lenght: %i\r\n\r\n"
                   , content_type, content_size);
  return(msg);
}

void    http_send_error(int status_code, http_request_t *request)
{
  nstr_t *tmp;

  tmp = nstr_make();
  
  tmp = nstr_addstr(tmp, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-15\">\n<title>%0%: %1%</title>\n<style type=\"text/css\">\nbody  {font-family: arial,sans-serif;}\nh1 {font-weight: bold; font-size: 140%;}\n</style>\n</head>\n<body>\n<h1>Error %0%: %1%</h1>\n</body>\n</html>\n");
  //tmp = nstr_file_read_all(tmp, "../templates/http.error.html");
  
  switch(status_code)
    {
    case 400:
      tmp = nstr_replace(tmp, "is", status_code, "Bad Request");
      break;
    case 401:
      tmp = nstr_replace(tmp, "is", status_code, "Unauthorized");
      break;
    case 403:
      tmp = nstr_replace(tmp, "is", status_code, "Forbidden");
      break;
    case 404:
      tmp = nstr_replace(tmp, "is", status_code, "Not Found");
      break;
    default:
      tmp = nstr_replace(tmp, "is", status_code, "Internal Server Error");
      break;
    }

  request->buff_out = http_make_header(request
                                       , status_code
                                       , "text/html"
                                       , tmp->write
                                       , request->buff_out);

  request->buff_out = nstr_addnstr(request->buff_out, tmp);
  //  printf("\n\nBuff_out:\n--\n%s\n--\n", request->buff_out->data);

  if(nstr_send(request->buff_out
               , request->socket
               , NANOD_SENDSIZE)
     != request->buff_out->write)
    {
      printf("send error not every thing send\n");
    }
  nstr_dispose(tmp);

}

// -1 on error
// o if ok
int     http_pars_header(http_request_t *request)
{
  nstr_t *tmp;
  nstr_t *header;
  nstrt_t *table;
  int   i;
  int   begin, end;
  int   error = 0;


  tmp = nstr_make();
  header = nstr_make();
  table = nstrt_make();

  header = nstr_addstr_n(header, request->buff_in->data, request->header_end);
  // let parse the top line first
  i = nstr_find_pos_begin(header
                          , "\r\n"
                          , 1
                          , 0);
  if(i != header->write)
    {
      tmp = nstr_addstr_n(tmp, header->data, i);
      table = nstr_split(table, tmp, " ");
      request->action = nstrt_getelem(table, 0);
      if(request->action == NULL)
        goto first_line_error;
      request->path = nstrt_getelem(table, 1);
      if(request->path == NULL)
        goto first_line_error;
      request->proto = nstrt_getelem(table, 2);
      if(request->proto == NULL)
        goto first_line_error;
    }
  else
    {

    first_line_error:

      nstr_dispose(tmp);
      nstrt_dispose_all(table);
      nstr_dispose(header);

      error = -1;
      goto end;
    }

  nstr_dispose(tmp);
  nstrt_dispose(table);

  for(i = 0; ; i = end)
    {
      tmp = nstr_make();
      begin = nstr_find_pos_after(header, "\r\n", 1, i);
      end = nstr_find_pos_begin(header, ": ", 1, begin);
      if(begin < header->write && end < header->write && end > begin)
        {
          tmp = nstr_addstr_n(tmp, header->data + begin, end - begin);
          request->head_keys = nstrt_addend(request->head_keys, tmp);
          tmp = nstr_make();
          begin = nstr_find_pos_after(header, ": ", 1, end);
          end = nstr_find_pos_begin(header, "\r\n", 1, begin);
          if(begin < header->write && end > begin)
            {
              tmp = nstr_addstr_n(tmp, header->data + begin, end - begin);
              request->head_values = nstrt_addend(request->head_values, tmp);
            }
          else
            {
              nstr_dispose(tmp);
              nstr_dispose(header);

              error = -1;
              goto end;
            }
        }
      else
        break;
    }
  nstr_dispose(header);

  // print all!
/*   printf("\nhttp_pars_header complete:\n"); */
/*   printf("action=%s\n", request->action->data); */
/*   printf("path=%s\n", request->path->data); */
/*   printf("proto=%s\n", request->proto->data); */
/*   for(i = 0; i < request->head_values->elements ; i++) */
/*     printf("key={%s} value={%s}\n" */
/*            , request->head_keys->table[i]->data */
/*            ,  request->head_values->table[i]->data); */

 end:

  return error;
}

// -1 read error
// -2 header too big
// -3 client msg error
int     http_get_header(http_request_t *request)
{
  int   n = 0;
  int   tmp = 0;

  request->header_end = 0;

 read_again:

  tmp = request->buff_in->write;
  request->buff_in = nstr_reserve(request->buff_in, NANOD_HTTP_READ_SIZE);
  n = read(request->socket, request->buff_in->data, NANOD_HTTP_READ_SIZE);
  request->buff_in->write = request->buff_in->write + n;
  printf("request->buff_in->write = %i\n", request->buff_in->write);
  request->buff_in->data[request->buff_in->write] = '\0';

  if(n == NANOD_HTTP_READ_SIZE)
    {
      printf("n == NANOD_HTTP_READ_SIZE");
      if((request->header_end = nstr_find_pos_begin(request->buff_in
                                                    , "\r\n\r\n"
                                                    , 1 // for first occurence
                                                    , tmp)) < request->buff_in->write)
        goto end_read;
      if (request->buff_in->write >= NANOD_HTTP_MAX_HEADER_SIZE)
        {
          printf("get_request_header: request->buff_in->write >= NANOD_HTTP_MAX_HEADER_SIZE\n");
          return -2;
        }
      else
        goto read_again;
    }
  if(n < 0)
    {
      printf("get_request_header: read error\n");
      return -1;
    }

 end_read:

  //printf("request :\n--\n%s\n--\n", request->buff_in->data);
  if(request->header_end == 0)
    {
      request->header_end = nstr_find_pos_begin(request->buff_in
                                                , "\r\n\r\n"
                                                , 1 // for first occurence
                                                , 0);
    }
  if(request->header_end < request->buff_in->write)
    {
      http_pars_header(request);
      request->host = nstr_make();
      for(n = 0;  n < request->head_keys->elements; n++)
        if(!strcmp(request->head_keys->table[n]->data, "Host"))
          request->host = nstr_addstr(request->host, request->head_values->table[n]->data);
      if(request->host->write == 0)
        {
          printf("request error: host not defined\n");
          return -3;
        }
      else
        printf("host: %s\n", request->host->data);
    }
  else
    {
      printf("request error: request->header_end < request->buff_in->write\n");
      return -3;
    }
  return 0;
}


nstr_t  *http_make_directory_line(nstr_t *listing, int type, int pos
                                  , nstr_t *host, nstr_t *path
                                  , const char *name)
{
  char  buff1[256];
  char  buff2[256];

  if(pos % 2 == 0)
    listing = nstr_addstr(listing, "<tr class=\"colorline\">");
  else
    listing = nstr_addstr(listing, "<tr>");

  if(type == NFILE_FT_DIRECTORY)
    listing = nstr_add(listing
                       , "<td><a href=\"http://%n%n/\">%s</a>/</td><td>Directory</td><td> - </td><td>%s</td></tr>\n"
                       , host
                       , path
                       , name
                       , nfile_get_date_last_modif_s(path->data, buff1, 256));
  else
    listing = nstr_add(listing
                       , "<td><a href=\"http://%n/%n\">%s</a></td><td>%s</td><td>%s</td><td>%s</td></tr>\n"
                       , host
                       , path
                       , name
                       , nfile_get_mime_type(path->data, "Unknow")
                       , nfile_get_size_s(path->data, buff2, 256)
                       , nfile_get_date_last_modif_s(path->data, buff1, 256));

  return listing;
}


// if -1 access denied
int     http_send_directory(http_request_t *request)
{
  struct dirent *entry;
  int   file_type;
  int   i, j;
  DIR   *dir;

  nstr_t *listing;
  nstr_t *html;
  nstr_t *file_path;
  nstr_t *title_bar;
  nstr_t *page_title;
  nstr_t *page_title_tmp;
  nstrt_t *url;

  if(!nfile_check_permission(request->path->data, 'x'))
    return(-1);

  dir = opendir(request->path->data);
  if(dir == NULL)
    return -1;

  listing = nstr_make();
  for(i = 0; (entry = readdir(dir)); i++)
    {
      if(strcmp(entry->d_name, "."))
        {
          file_path = nstr_make();
          file_path = nstr_addnstr(file_path, request->path);
          file_path = nstr_addstr(file_path, entry->d_name);

          file_type = nfile_get_type(file_path->data);
          switch(file_type)
            {
            case NFILE_FT_REGFILE:
              listing = http_make_directory_line(listing
                                                 , NFILE_FT_REGFILE
                                                 , i
                                                 , request->host
                                                 , file_path
                                                 , entry->d_name);
              break;
            case NFILE_FT_DIRECTORY:
              listing = http_make_directory_line(listing
                                                 , NFILE_FT_DIRECTORY
                                                 , i
                                                 , request->host
                                                 , file_path
                                                 , entry->d_name);
              break;
            default:
              break;
            }
          nstr_dispose(file_path);
        }
      else i++;
    }
  // title bar
  title_bar = nstr_make();
  title_bar = nstr_add(title_bar
                       , "Directory index of %n"
                       , request->host);
  if(request->path->data[0] != '/')
    title_bar = nstr_addchar(title_bar, '/');
  title_bar = nstr_addnstr(title_bar, request->path);
  if(request->path->data[request->path->write - 1] != '/')
    title_bar = nstr_addchar(title_bar, '/');

  // page title
  page_title = nstr_make();
  page_title = nstr_add(page_title
                        , "Directory index of <a href=\"http://%n/\">%n</a>/"
                        , request->host
                        , request->host);

  url = nstrt_make();
  url = nstr_split(url, request->path, "/");
  for(i = 0; url->table[i]; i++)
    {
      page_title_tmp = nstr_make();
      page_title_tmp = nstr_add(page_title_tmp
                                , "<a href=\"http://%n/"
                                , request->host);
      for(j = 0; j <= i; j++)
        {
          page_title_tmp = nstr_add(page_title_tmp
                                    , "%n/"
                                    , url->table[j]);
        }
      page_title_tmp = nstr_add(page_title_tmp
                                , "\">%n</a>/"
                                , url->table[i]);
      page_title = nstr_addnstr(page_title, page_title_tmp);
      nstr_dispose(page_title_tmp);
    }
  nstrt_dispose(url);

  html = nstr_make();

  
  html = nstr_addstr(html, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-15\">\n<title>%0%</title>\n<style type=\"text/css\">\nbody  {font-family: arial,sans-serif; font-size: 1em;}\nh1 {font-weight: bold; font-size: 1.2em;}\na:link, a:visited {text-decoration: none; }\na:hover {text-decoration: underline;}\nth {text-align: left;padding: 0.4em;}\ntd {padding: 0.3em;}\nthead {border-bottom: 5px;}\n.colorline {background-color: #E5ECF9;}\n</style>\n</head>\n<body>\n<h1>%1%</h1>\n<hr>\n<table>\n  <tr>\n    <th>Name</th><th>Type</th><th>Size</th><th>Last modification date</th>\n  </tr>\n  %2%\n</table>\n</body>\n</html>");
 
/*   html = nstr_file_read_all(html, "../templates/http.dirlist.html"); */

  html = nstr_replace(html, "nnn", title_bar, page_title, listing);

  nstr_dispose(title_bar);
  nstr_dispose(page_title);
  nstr_dispose(listing);

  // now make header and send
  request->buff_out = http_make_header(request
                                       , 200
                                       , "text/html"
                                       , html->write
                                       , request->buff_out);
  request->buff_out = nstr_addnstr(request->buff_out, html);
  nstr_send(request->buff_out
            , request->socket
            , NANOD_SENDSIZE);

  nstr_dispose(html);
  return 0;

}


// return -1 if access denied
// -2 if read error
int     http_send_file(http_request_t *request)
{
  nstrfile_t *file;
  int   n, ns;
  nstr_t *tmp;

  char  buff[64];
  int   i;

  printf("http_send_file 1\n");
  
  // make header and send
  request->buff_out = http_make_header(request
                                       , 200
                                       , nfile_get_mime_type(request->path->data
                                                             , "application/octet-stream")
                                       , -1
                                       , request->buff_out);
    
  file = nstr_open_file(request->path->data);
  tmp = nstr_make();
  
 read_chunk:

  
  tmp = nstr_reserve(tmp
                     , NANOD_HTTP_FILE_READ);
  n = fread(tmp->data
            , 1
            , NANOD_HTTP_FILE_READ
            , file->stream);
  if(n < 0)
    {
      printf("read_error\n");
      nstr_dispose(tmp);
      return -2;
    }
  
  //printf("http_send_file 5\n");
  tmp->write = n;
  
  i = sprintf(buff, "%x", n);
  buff[i] = '\0';
  
  request->buff_out = nstr_add(request->buff_out, "%s\r\n%n\r\n\r\n", buff, tmp);
  ns = nstr_send(request->buff_out
                 , request->socket
                 , NANOD_HTTP_FILE_SEND);
  
  //printf("http_send_file 6\n");
  
  request->buff_out = nstr_clean(request->buff_out);
  
  if(n == NANOD_HTTP_FILE_READ)
    goto read_chunk;
  else
    {
      request->buff_out = nstr_add(request->buff_out, "0\r\n\r\n");
      nstr_send(request->buff_out
                , request->socket
                , NANOD_HTTP_FILE_SEND);
      printf("last chunck:\n__\n%s\n--\n", request->buff_out->data);
    }
  
  //printf("http_send_file 7\n");
  fclose (file->stream);
  free(file);
  return 0;
  
  
}


int     http(void *params, int socket)
{
  int   file_type;
  http_request_t request;

  request.buff_in = nstr_make();
  request.buff_out = nstr_make();

  request.head_keys = nstrt_make();
  request.head_values = nstrt_make();


  request.socket = socket;
  if (http_get_header(&request) < 0)
    {
      http_send_error(400, &request);
      goto dispose_buff;
    }

  file_type = nfile_get_type(request.path->data);
  switch(file_type)
    {
    case NFILE_FT_DIRECTORY:
      if(nfile_check_permission(request.path->data, 'x'))
        {
          if(http_send_directory(&request) < 0)
            // error
            http_send_error(500, &request);
        }
      else
        // forbiden
        http_send_error(403, &request);
      break;
    case  NFILE_FT_REGFILE:
      if(nfile_check_permission(request.path->data, 'r'))
        {
          if(http_send_file(&request) < 0)
            // error
            http_send_error(500, &request);
        }
      else
        // forbiden
        http_send_error(403, &request);
      break;
    default:
      http_send_error(404, &request);
      break;
    }

  nstr_dispose(request.action);
  nstr_dispose(request.path);
  nstr_dispose(request.proto);
  nstr_dispose(request.host);

 dispose_buff:

  nstrt_dispose_all(request.head_keys);
  nstrt_dispose_all(request.head_values);
  nstr_dispose(request.buff_in);
  nstr_dispose(request.buff_out);

  // close connection
  return -1;
}

